package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/golang/protobuf/proto"
	"github.com/streadway/amqp"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
	miProto "zyb/com_muchinfo_mtp_proto"
	fwk "zyb/framework"
	"zyb/zybPublic"
)

var (
	exitChan chan os.Signal
)

func main() {
	fmt.Println("------------start testClient--------")

	// catch exit signal.
	{
		exitChan = make(chan os.Signal)
		signal.Notify(exitChan, os.Interrupt, os.Kill,
			syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
		go exitHandle()
	}

	// init framework.
	if !fwk.Init() {
		fmt.Println("init framework fail.")
		os.Exit(2)
	}

	fwk.Log().Logger().Debug("start testclient")
	regMqMessage()

	fwk.Start()

	initMenu()
	loopMenu()

	fmt.Println("------------exit testClient")
	fwk.Log().Logger().Debug("end testclient")
	fwk.Stop()
}

func exitHandle() {
	s := <-exitChan
	fwk.Log().Logger().Debugln("recv exit signal:", s)
	fwk.Stop()
	os.Exit(1)
}

func regMqMessage() {
	fwk.Mq().RegMsgHandleEx(onMessageEx)
	//fwk.Mq().SubscribTopic("bm_quote")
}

func initMenu() {
	addToMenu("add", add)
	addToMenu("sendSPSLSetChangeNtf", sendSPSLSetChangeNtf)
	addToMenu("sendManageClearPos", sendManageClearPosReq)
	addToMenu("loadFollowOrderSet", LoadFollowOrderSet)
	addToMenu("sendChannelOrderReq", SendChannelOrderReq)
	addToMenu("SendChannelOrderReqFromCfg", SendChannelOrderReqFromCfg)
	addToMenu("sendCancelOrderReq", SendCancelOrderReq)
	addToMenu("sendRealtimeQuote", SendRealtimeQuote)
	addToMenu("sendSPSLOrderReq", SendSPSLOrder)

	if fwk.IsInitRedis() {
		addToMenu("readReidsQuote", readRedisQuote)
	}
}

/*func onMessage(data []byte) {
	var rq miProto.RealtimeQuote

	fcode := zybPublic.BytesToUint32(data[:4])
	sid := zybPublic.BytesToInt(data[4:8])

	fmt.Printf("recv msg funcode[%d] sid[%d]\n", fcode, sid)

	if fcode == FID_RealtimeQuote {
		err := proto.Unmarshal(data[8:], &rq)
		if err != nil {
			fmt.Println("Unmarshal RealtimeQuote fail:", err)
			return
		}

		printJson(rq)
	}
}*/

func onMessageEx(msg amqp.Delivery) {
	fCode := zybPublic.BytesToInt(msg.Body[:4])
	sid := zybPublic.BytesToInt(msg.Body[4:8])

	if fCode == 18022401 {
		var rq miProto.RealtimeQuote
		err := proto.Unmarshal(msg.Body[8:], &rq)
		if err != nil {
			fmt.Println("Unmarshal RealtimeQuote fail:", err)
			return
		}
		fwk.Log().Logger().Debugf("recv msg funcode[%d] sid[%d]\n%s", fCode, sid, getJsonString(rq))
		_ = msg.Ack(true)
	}
}

func add() {
	fmt.Println("I am add func.")
}

func add2(a, b int) int {
	return a + b
}

func printJson(arg interface{}) {
	f, err := json.Marshal(arg)
	if err != nil {
		fmt.Println("json Marshal fail:", err)
		return
	}

	var out bytes.Buffer
	err = json.Indent(&out, f, "", "    ")
	if err != nil {
		fmt.Println("json Indent fail:", err)
		return
	}

	fmt.Printf("%T\n", arg)
	fmt.Println(out.String())
}

func getJsonString(arg interface{}) string {
	f, err := json.Marshal(arg)
	if err != nil {
		fmt.Println("json Marshal fail:", err)
		return ""
	}

	var out bytes.Buffer
	err = json.Indent(&out, f, "", "    ")
	if err != nil {
		fmt.Println("json Indent fail:", err)
		return ""
	}

	return out.String()
}

func storeToFile(filename string, arg interface{}) {
	fullName := "./jsondata/"+filename
	f, err := os.OpenFile(fullName, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		fmt.Printf("open file[%s] fail,err:%v", fullName, err)
		return
	}
	defer f.Close()
	str := getJsonString(arg)
	_, err = f.WriteString(str)
	if err != nil {
		fmt.Printf("write file[%s] fail.err:%v", fullName, err)
		return
	}
}

func readFromFile(filename string, arg interface{}) {
	fullName := "./jsondata/"+filename
	f, err := os.OpenFile(fullName, os.O_RDONLY, 0755)
	if err != nil {
		fmt.Printf("open file[%s] fail,err:%v", fullName, err)
		return
	}
	defer f.Close()
	buf := make([]byte, 10000)
	var nCount int = 0
	nCount, err = f.Read(buf)
	if err != nil {
		fmt.Printf("read file[%s] fail.err:%v", fullName, err)
		return
	}

	buf = buf[0:nCount]

	//fmt.Println("json:\n",string(buf))
	err = json.Unmarshal(buf, arg)
	if err != nil {
		fmt.Println("unmarshal json fail:", err)
	}
}

func confirmMsg() bool {

	fmt.Print("send this message? (y or n):")
	var strIn string
	_, err := fmt.Scanln(&strIn)
	if err != nil {
		fmt.Println("invalid input.")
		return false
	}

	strings.ToLower(strIn)

	if strIn != "y" {
		fmt.Println("you cancel send this message.")
		return false
	}

	return true
}

func sendSPSLSetChangeNtf() {
	fmt.Println("send spslset changeNtf")

	ntf := miProto.SPSLSetChangeNtf{}

	var funCode uint32 = 131156
	var sid uint32 = 7788
	ntf.Header = new(miProto.MessageHead)
	ntf.Header.FunCode = proto.Uint32(funCode)
	ntf.SpslId = proto.Uint64(1)
	ntf.OperateType = proto.Uint32(1)

	printJson(ntf)
	fwk.Mq().PublishMessage("manager_ntf", funCode, sid, &ntf)
}

func sendManageClearPosReq() {
	var accountId uint64 = 0

	fmt.Printf("\n[uint64] accountid:")
	_, err := fmt.Scanln(&accountId)
	if err != nil {
		fmt.Println("invalid input:", err)
		return
	}

	req := miProto.ManageClearPosReq{
		AccountID: proto.Uint64(accountId),
	}

	var funCode uint32 = 196679
	var sid uint32 = 7788
	req.Header = new(miProto.MessageHead)
	req.Header.FunCode = proto.Uint32(funCode)
	printJson(req)

	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("order_req", funCode, sid, &req)
}

func readRedisQuote() {

	// input goodscode.
	var goodscode string
	{
		fmt.Printf("\ninput goodscode:")
		_, err := fmt.Scanln(&goodscode)
		if err != nil {
			fmt.Println("invalid input.")
			return
		}
	}

	// get quote.
	{
		strKey := "QUOTE:" + goodscode
		data, err := redis.Bytes(fwk.Rds().Conn().Do("get", strKey))
		if err != nil {
			fmt.Println("get quote from redis fail:", err)
			return
		}

		var gp miProto.RealtimeQuote
		{
			err := proto.Unmarshal(data, &gp)
			if err != nil {
				fmt.Println("Unmarshal RealtimeQuote fail:", err)
				return
			}
		}

		// json print.
		{
			buf, err := json.Marshal(gp)
			if err != nil {
				fmt.Println("json print fail:", err)
			}

			var szBuf bytes.Buffer
			err = json.Indent(&szBuf, buf, "", "    ")
			if err != nil {
				fmt.Println("json err.", err)
				return
			}

			//fmt.Printf("\n----goodscode[%s] quote---------\n%s", goodscode, gp.String())
			fmt.Printf("\n-----json--goodscode[%s] quote-------\n", goodscode)
			fmt.Println(szBuf.String())
		}
	}
}

func LoadFollowOrderSet() {
	sqlId := `SELECT t.STRATEGYNAME, t.AUTOID, t.TYPE, t.PRICEMODE, t.RATIO, t.DIRECT,
    t.ExcuteAccSrc, t.ACCOUNTID, t.CREATETIME FROM imp_followorderset AS t`
	var data []zybPublic.FollowOrderSet /* = make([]zybPublic.FollowOrderSet, 100)*/
	err := fwk.MySQL().Conn().Select(&data, sqlId)
	if err != nil {
		fmt.Println("select record faild:", err)
		return
	}

	fmt.Println("====================FollowOrderSet data============")
	for _, t := range data {
		printJson(t)
		fmt.Print("\n-----------------------\n")
		fwk.Log().Logger().Debug(getJsonString(t))
	}
}

func InputField(a interface{}, filedName, label string) {

	if a == nil {
		fmt.Println("nil point:", filedName)
		return
	}

	if label != "" {
		fmt.Printf("%s\n[%T] %s:", label, a, filedName)
	} else {
		fmt.Printf("[%T] %s:", a, filedName)
	}

	_, _ = fmt.Scanln(a)
}

func SendChannelOrderReq() {

	t := time.Now().Unix()

	req := miProto.ChannelOrderReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_ChannelOrderReq),
			RequestID:  proto.Uint32(0),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String("test_001"),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		AccountID:          proto.Uint64(0),
		GoodsID:            proto.Uint32(0),
		MarketID:           proto.Uint32(15201),
		OrderQty:           proto.Uint64(1),
		OrderPrice:         proto.Float64(0),
		ClientSerialNo:     proto.String(strconv.FormatInt(t, 10)),
		ClientOrderTime:    proto.String(time.Now().Format("2006-01-02 15:04:05")),
		ClientType:         proto.Uint32(2),
		ChannelBuildType:   proto.Uint32(1),
		BuyOrSell:          proto.Uint32(0),
		PriceMode:          proto.Uint32(2),
		HedgeFlag:          proto.Uint32(0),
		TimeValidType:      proto.Uint32(1),
		ChannelOperateType: proto.Uint32(1),
		ChannelOrderSrc:    proto.Uint32(1),
		ValidType:          proto.Int32(1),
		SPPoint:            proto.Uint32(0),
		SLPoint:            proto.Uint32(0),
	}
	
	InputField(req.AccountID, "accountid", "")
	InputField(req.GoodsID, "goodsid", "")
	InputField(req.MarketID, "marketid", "")
	InputField(req.OrderQty, "orderqty", "")

	InputField(req.ChannelBuildType, "buildtype", "下单类型(开平标志 - 0:无 1:建仓 2:平仓 3:先平后建)")
	InputField(req.BuyOrSell, "buyorsell", "买卖方向(买卖 - 0:买 1:卖 )")
	InputField(req.PriceMode, "pricemode", "取价方式 - 1:市价 2: 限价")

	if req.PriceMode != nil && *req.PriceMode == 2 {
		InputField(req.OrderPrice, "orderprice", "")
	}

	if req.ChannelBuildType != nil && *req.ChannelBuildType == 2 {
		InputField(req.CloseType, "closetype", "平仓方式(平仓方式 - 0:无 1:平今 2:平昨)")
	}

	if req.CloseType != nil && *req.CloseType == 1 {
		InputField(req.CloseTodayQty, "closetodayqty", "平今数量")
	}

	InputField(req.SPPoint, "sppoint", "止盈点数")
	InputField(req.SLPoint, "slpoint", "止损点数")
	InputField(req.ChannelOrderSrc, "ordersrc", "单据来源(1-客户端 5-交易 6-跟单)")

	// header.
	*req.Header.AccountID = *req.AccountID
	*req.Header.GoodsID = *req.GoodsID
	*req.Header.MarketID = *req.MarketID

	printJson(req)
	storeToFile("order.json", req)

	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("order_req", *req.Header.FunCode, 7788, &req)

}

func SendChannelOrderReqFromCfg() {
	t := time.Now().Unix()

	req := miProto.ChannelOrderReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_ChannelOrderReq),
			RequestID:  proto.Uint32(0),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String("test_001"),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		AccountID:          proto.Uint64(0),
		GoodsID:            proto.Uint32(0),
		MarketID:           proto.Uint32(15201),
		OrderQty:           proto.Uint64(1),
		OrderPrice:         proto.Float64(0),
		ClientSerialNo:     proto.String(strconv.FormatInt(t, 10)),
		ClientOrderTime:    proto.String(time.Now().Format("2006-01-02 15:04:05")),
		ClientType:         proto.Uint32(2),
		ChannelBuildType:   proto.Uint32(1),
		BuyOrSell:          proto.Uint32(0),
		PriceMode:          proto.Uint32(2),
		HedgeFlag:          proto.Uint32(0),
		TimeValidType:      proto.Uint32(1),
		ChannelOperateType: proto.Uint32(1),
		ChannelOrderSrc:    proto.Uint32(1),
		ValidType:          proto.Int32(1),
		SPPoint:            proto.Uint32(0),
		SLPoint:            proto.Uint32(0),
	}

	readFromFile("order.json", &req)
	printJson(req)
	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("order_req", *req.Header.FunCode, 7788, &req)
}

func SendCancelOrderReq() {

	t := time.Now().Unix()

	req := miProto.CancelOrderReq{
		Header: &miProto.MessageHead{
			FunCode:       proto.Uint32(FID_CancelOrderReq),
			RequestID:     proto.Uint32(0),
			AccountID:     proto.Uint64(0),
			AccessID:      proto.Uint32(0),
			ClientTime:    proto.Int64(t),
			GoodsID:       proto.Uint32(0),
			UUID:          proto.String("test_cancelorderreq_001"),
			MarketID:      proto.Uint32(15201),
			UserID:        proto.Uint32(0),
			ResponseTopic: proto.String("order_rsp"),
			AccountID2:    proto.Uint64(0),
		},
		ClientSerialNo:  proto.String(""),
		ClientOrderTime: proto.String(""),
		ClientType:      proto.Uint32(2),
		OperateType:     proto.Uint32(1),
		OldOrderId:      proto.Uint64(0),
		AccountID:       proto.Uint64(0),
		OrderSrc:        proto.Uint32(1),
		OperatorID:      proto.Uint64(1),
	}

	InputField(req.Header.MarketID, "marketId", "")
	InputField(req.Header.GoodsID, "goodsId", "")
	InputField(req.AccountID, "accountId", "")
	InputField(req.OldOrderId, "oldorderid", "被撤的委托单ID")

	// header.
	*req.Header.AccountID = *req.AccountID

	printJson(req)

	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("order_req", *req.Header.FunCode, 7788, &req)
}

func SendRealtimeQuote() {
	var goodsCode = new(string)
	InputField(goodsCode, "goodsCode", "Input goodsCode")
	fName := *goodsCode + ".xml"
	q, err := ReadQuoteFromFile(fName)
	if err != nil {
		fmt.Println("read quote from file fail:", err)
		return
	}

	q.GoodsCode = proto.String(*goodsCode)

	printJson(q)

	askPrice := proto.Uint64(0)
	bidPrice := proto.Uint64(0)
	quoteUnit := proto.Uint64(0)

	InputField(bidPrice, "bidPrice", "")
	InputField(askPrice, "askPrice", "")
	InputField(q.Last, "lastprice", "")
	InputField(q.LastVolume, "lastVolume", "")
	InputField(quoteUnit, "quoteUnit", "行情跳动单位")

	if *quoteUnit > 0 {
		for i := 0; i < 5; i++ {
			q.BidPriceArray = append(q.BidPriceArray, *bidPrice-(*quoteUnit)*uint64(i))
			q.AskPriceArray = append(q.AskPriceArray, *askPrice+(*quoteUnit)*uint64(i))
			q.BidVolumeArray = append(q.BidVolumeArray, 100)
			q.AskVolumeArray = append(q.AskVolumeArray, 100)
		}
	}

	*q.DateTime = time.Now().Format("20060102 150405")
	*q.LastVolume = uint64(time.Now().Unix() % 1000000)
	*q.TotalVolume = *q.LastVolume

	printJson(q)

	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("bm_quote", *q.Header.FunCode, 7788, q)
}

func inArray(a uint32, args ...uint32) bool {
	for _, arg := range args {
		if a == arg {
			return true
		}
	}

	return false
}

func SendSPSLOrder() {
	t := time.Now().Unix()

	req := miProto.ChannelSPSLOrderReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_ChannelSPSLOrderReq),
			RequestID:  proto.Uint32(0),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String("test_spsl_001"),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		ClientType:  proto.Uint32(2),
		LoginID:     proto.Uint64(0),
		AccountID:   proto.Uint64(0),
		GoodsID:     proto.Uint32(0),
		MarketID:    proto.Uint32(15201),
		ValidType:   proto.Int32(1),
		OperateType: proto.Uint32(1),
		OrderSrc:    proto.Uint32(1),
		OrderQty:    proto.Uint64(0),
		BuyOrSell:   proto.Uint32(0),
		RelatedID:   proto.Uint64(0),
		SpPrice:     proto.Float64(0),
		SlPrice:     proto.Float64(0),
		PriceType:   proto.Uint32(1),
		TriggerType: proto.Uint32(1),
	}

	InputField(req.AccountID, "accountid", "")
	InputField(req.GoodsID, "goodsid", "")
	InputField(req.MarketID, "marketid", "")
	InputField(req.OperateType, "operatetype", "1-止盈 2-止损 3-撤单 4-止盈止损 6-重新激活 7-修正")

	opType := *req.OperateType
	if inArray(opType, 1, 2, 4) {
		InputField(req.PriceType, "pricetype", "1-最新价 2-对手价 3-市价")
		InputField(req.BuyOrSell, "buyorsell", "0:买 1:卖")

		if inArray(opType, 1, 4) {
			InputField(req.SpPrice, "spprice", "止盈价格")
		}

		if inArray(opType, 2, 4) {
			InputField(req.SlPrice, "slprice", "止损价格")
		}

		InputField(req.OrderQty, "orderqty", "")
	}

	if inArray(opType, 3, 6, 7) {
		InputField(req.RelatedID, "relateId", "关联单号")
	}

	InputField(req.OrderSrc, "ordersrc", "单据来源(1-客户端 5-交易 6-跟单)")

	// header.
	*req.Header.AccountID = *req.AccountID
	*req.Header.GoodsID = *req.GoodsID
	*req.Header.MarketID = *req.MarketID

	printJson(req)

	if !confirmMsg() {
		return
	}

	fwk.Mq().PublishMessage("order_req", *req.Header.FunCode, 7788, &req)
}
